﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.InteropServices;
using System.Collections;
using System.IO.MemoryMappedFiles;

namespace Liquifier
{
    public partial class TypeData
    {
        protected enum Action
        {
            Continue,
            Break,
            SoftInclude,
            HardInclude
        }

        protected Action ProcessMemberAttributes(IEnumerable<LiquifyAttribute> attributes, LiquifiableAttribute settings)
        {
            Action action = Action.Continue;

            foreach (LiquifyAttribute att in attributes)
            {
                switch (att.Options)
                {
                    case SerialisationOptions.Default:
                    case SerialisationOptions.SerializeOnly:
                        action = Action.SoftInclude;
                        break;

                    case SerialisationOptions.NonSerialized:
                        action = Action.Break;
                        break;

                    case SerialisationOptions.UpdateExistingIList:
                        break;
                }
            }

            return action;
        }

        public MemberCollection FilterMembers(LiquifiableAttribute settings)
        {
            if (!cachedFilteredMembers.ContainsKey(settings.GetNonCollidingHashCode()))
            {
                MemberCollection filtered = new MemberCollection();
                foreach (Member m in FilterMembers(this.Members.MembersList, settings))
                {
                    if (!filtered.Contains(m.Name))
                        filtered.Add(m);
                }
                cachedFilteredMembers.Add(settings.GetNonCollidingHashCode(), filtered);
            }
            return cachedFilteredMembers[settings.GetNonCollidingHashCode()];
        }

        private Dictionary<int, MemberCollection> cachedFilteredMembers = new Dictionary<int, MemberCollection>();

        IEnumerable<Member> FilterMembers(IEnumerable<Member> members, LiquifiableAttribute settings)
        {
            foreach (Member i in members)
            {
                if (settings.IncludeMembers != null && settings.IncludeMembers.Contains(i.Name))
                    yield return i;

                if (i.IsIndexed)
                    continue;

                if (i.IsLiteral)
                    continue;

                switch (ProcessMemberAttributes(i.Attributes, settings))
                {
                    case Action.Continue:
                        break;

                    case Action.Break:
                        continue;

                    case Action.SoftInclude:
                        if (i.IsField)
                        {
                            if ((settings.FieldFlags & Serialisation.Explicit) > 0)
                                yield return i;
                        }
                        if (i.IsProperty)
                        {
                            if ((settings.PropertyFlags & Serialisation.Explicit) > 0)
                                yield return i;
                        }
                        break;

                    case Action.HardInclude:
                        yield return i;
                        break;
                }

                if (i.IsNotSerialized)
                    continue;

                if (i.Name.Contains(">k__BackingField"))    //cull backing fields (this way until we come up with a better one)
                    continue;

                if (i.IsTypeOf(typeof(Delegate)))
                {
                    if ((settings.SpecialTypeFlags & TypeFlags.DelegatesAndEvents) > 0)
                        yield return i;
                    else
                        continue;
                }

                /* Here is our concession to the user model: the user includes/excludes members categorised into Fields or Properties so must make that distinction
                 * once here at least */

                if (i.IsField)
                {
                    if (i.IsPublic && ((settings.FieldFlags & Serialisation.Public) > 0))
                        yield return i;

                    if (i.IsPrivate && ((settings.FieldFlags & Serialisation.Private) > 0))
                        yield return i;
                }

                if (i.IsProperty)
                {
                    if (i.IsPublic && ((settings.PropertyFlags & Serialisation.Public) > 0) && i.IsReadable && i.IsWriteale )
                        yield return i;                
                }

            }
        }
    }
}
